import axios, { type AxiosInstance, type AxiosRequestConfig, type InternalAxiosRequestConfig, type AxiosResponse } from "axios";
import { emitter } from "@/utils/emitter";
import { authStore } from "@/store";

const baseURL = import.meta.env.VITE_BASE_URI;

/**
 * 封装 Axios 的 HTTP 客户端类
 * 实现功能：
 * 1. 基础请求封装
 * 2. JWT 自动刷新
 * 3. 请求重试队列
 * 4. 统一错误处理
 */
class Http {
    // Axios 实例
    private instance: AxiosInstance;
    // 是否正在刷新 token 的标志
    private isRefreshing = false;
    // 存储待重试的请求队列
    private pendingRequests: ((token: string) => void)[] = [];

    constructor() {
        // 创建 Axios 实例
        this.instance = axios.create({
            baseURL: baseURL, // 从环境变量读取基础URL
            //设置withCredentials为true，以便浏览器可以发送和接收Cookie
            withCredentials: true,
            //超时时间3分钟
            timeout: 1000 * 60 * 3,
        });

        // 设置拦截器
        this.setupInterceptors();
    }

    /**
     * 设置请求和响应拦截器
     */
    private setupInterceptors(): void {
        //请求拦截器
        this.instance.interceptors.request.use(
            (config: InternalAxiosRequestConfig) => {
                //从状态管理里获取token
                const token = authStore()?.accessToken;

                if (config && config.headers && token) {
                    config.headers.Authorization = token;
                    //打开加载层
                    emitter.emit("loading-open");
                } else {
                    emitter.emit("msg-error", "暂无认证信息，请先登录！");
                    emitter.emit("router-push", "/");
                }

                // 处理完毕，返回config对象
                return config;
            },
            (error) => {
                // 请求错误处理（如网络错误）
                return Promise.reject(error);
            }
        );

        //响应拦截器
        this.instance.interceptors.response.use(
            async (response: AxiosResponse) => {
                //关闭加载层
                emitter.emit("loading-close");
                //获取响应状态码
                const code = response?.data?.code;
                if (response?.data?.success && code === 200) {
                    return response?.data;
                } else if (code === 401 || code === 404) {
                    emitter.emit("router-push", `/${code}`);
                } else if (code === 4001) {
                    //需要刷新token
                    return this.handleTokenExpired(response);
                } else if (code === 500) {
                    emitter.emit("msg-error", response?.data?.message);
                    return Promise.reject(response);
                } else {
                    emitter.emit("msg-warning", response?.data?.message);
                    return Promise.reject(response);
                }
            },
            (error) => {
                // 其他错误直接拒绝
                return Promise.reject(error);
            }
        );
    }
    /**
     * 处理 token 过期逻辑
     * @param response 原始响应对象
     * @returns Promise
     */
    private async handleTokenExpired(response: any): Promise<any> {
        // 获取原始请求配置
        const originalRequest = response.config;

        // 标记这个请求已经重试过，防止无限循环
        originalRequest._isRetryRequest = true;

        // 如果已经在刷新 token，将当前请求加入队列等待
        if (this.isRefreshing) {
            return new Promise((resolve) => {
                this.pendingRequests.push((newToken: string) => {
                    // 用新 token 更新请求头
                    originalRequest.headers.Authorization = newToken;
                    // 重新发送请求
                    resolve(this.instance(originalRequest));
                });
            });
        }

        // 设置刷新标志为 true
        this.isRefreshing = true;

        //状态库
        const store = authStore();

        try {
            // 调用刷新 token 的接口
            const data = await axios.post(`${baseURL}/refresh`, {}, { withCredentials: true, headers: { "X-Refresh-Token": store.refreshToken } });

            // 存储新的 tokens
            store.setAccessToken(data?.headers["authorization"]);

            // 更新原始请求的 token
            originalRequest.headers.Authorization = store.accessToken;

            // 重新发送原始请求
            const retryResponse = await this.instance(originalRequest);

            // 执行队列中的等待请求
            this.pendingRequests.forEach((callback) => callback(store.accessToken));
            this.pendingRequests = []; // 清空队列

            return retryResponse;
        } catch (refreshError) {
            emitter.emit("msg-error", "登录过期，请重新登录");
            store.clear();
            // 刷新 token 失败，跳转到登录页
            emitter.emit("router-push", "/");
            return Promise.reject(refreshError);
        } finally {
            // 重置刷新标志
            this.isRefreshing = false;
        }
    }

    /**
     * 通用请求方法
     * @param config Axios 请求配置
     * @returns Promise 包含响应数据
     */
    public request<T = any>(config: AxiosRequestConfig): Promise<T> {
        return this.instance.request(config);
    }

    /**
     * GET 请求
     * @param url 请求地址
     * @param params 请求参数
     * @param config 可选配置
     * @returns 响应数据中的 data
     */
    public get<T = any>(url: string, params = {}, config?: AxiosRequestConfig): Promise<T> {
        return this.request({ url: url, params: params, method: "get", ...config })
            .then((res) => res?.data)
            .catch((err) => console.error(err.data));
    }

    /**
     * POST 请求
     * @param url 请求地址
     * @param params 请求数据
     * @param config 可选配置
     * @returns Promise 包含响应数据
     */
    public post<T = any>(url: string, params = {}, config?: AxiosRequestConfig): Promise<T> {
        return this.request({ method: "post", url, data: params, ...config });
    }

    /**
     * PUT 请求
     * @param url 请求地址
     * @param params 请求数据
     * @param config 可选配置
     * @returns Promise 包含响应数据
     */
    public put<T = any>(url: string, params = {}, config?: AxiosRequestConfig): Promise<T> {
        return this.request({ method: "put", url, data: params, ...config });
    }

    /**
     * DELETE 请求
     * @param url 请求地址
     * @param params 请求参数
     * @param config 可选配置
     * @returns Promise 包含响应数据
     */
    public delete<T = any>(url: string, params = {}, config?: AxiosRequestConfig): Promise<T> {
        return this.request({ url: url, params: params, method: "delete", ...config });
    }

    /**
     * UPLOAD 请求
     * @param url 请求地址
     * @param file 要上传的文件
     * @param config 可选配置
     * @returns Promise 包含响应数据
     */
    public upload<T = any>(url: string, file: any, config?: AxiosRequestConfig): Promise<T> {
        const form = new FormData();
        form.append("file", file);
        return this.post(url, form, config);
    }
    /**
     * EXPORT 请求
     * @param url 请求地址
     * @param params 请求参数
     * @param config 可选配置
     * @returns Promise 包含响应数据
     */
    public export(url: string, params = {}, config?: AxiosRequestConfig): void {
        emitter.emit("loading-open");
        const auth = authStore();
        const defaultConfig: AxiosRequestConfig<{}> = { responseType: "blob", headers: { Authorization: auth.accessToken, "Content-Type": "application/json" } };
        axios.post(baseURL + url, params, { ...config, ...defaultConfig }).then((response) => {
            emitter.emit("loading-close");
            if (response?.status === 200) {
                if (response.headers["content-type"] === "application/json") {
                    //后端响应状态码，json数据
                    const reader = new FileReader();
                    reader.readAsText(response.data, "utf-8");
                    reader.onload = () => {
                        const result = JSON.parse(reader.result!.toString());
                        if (result?.code === 401 || result?.code === 404) {
                            emitter.emit("router-push", `/${result?.code}`);
                        } else if (result?.code === 4001) {
                            this.handleTokenExpired(response);
                        } else {
                            emitter.emit("msg-error", result.message);
                        }
                    };
                } else {
                    this.download(response);
                }
            } else {
                emitter.emit("msg-error", "导出失败");
            }
        });
    }

    /**
     * 下载文件
     * @param response 响应数据
     */
    private download(response: AxiosResponse): void {
        const link = document.createElement("a");
        const blob = new Blob([response.data], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
        const downloadUrl = URL.createObjectURL(blob);
        link.style.display = "none";
        link.href = downloadUrl;

        // 处理文件名
        const fileName = this.getFilenameFromHeaders(response?.headers, "xlsx");
        link.setAttribute("download", fileName);
        document.body.appendChild(link);
        link.click();
        // 清理
        setTimeout(() => {
            document.body.removeChild(link);
            window.URL.revokeObjectURL(downloadUrl);
        }, 100);
    }

    /**
     * 从请求头中获取文件名
     */
    private getFilenameFromHeaders(headers: any, suffix: string): string {
        const disposition = headers["content-disposition"];
        if (!disposition) return `export_${Date.now()}.${suffix}`;

        // 处理RFC 5987编码文件名（推荐优先使用）
        const utf8FilenameMatch = disposition.match(/filename\*=UTF-8''([^;]+)/);
        if (utf8FilenameMatch) {
            return decodeURIComponent(utf8FilenameMatch[1]);
        }

        // 精确匹配文件名
        const filenameMatch = disposition.match(/filename="?([^";]+)"?/);
        return filenameMatch ? filenameMatch[1] : `export_${Date.now()}.${suffix}`;
    }
}

const http = new Http();

export { baseURL, http, axios };
